﻿/*
    类 说 明：业务逻辑类，封装了数据表相关基本操作
	生成时间：2021/4/16 16:24:05
	生成工具：Kalman Studio
*/

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Threading.Tasks;

using Kalman;
using Kalman.Extensions;
using RestSharp;
using Newtonsoft.Json.Linq;
using System.Security.Cryptography;
using RestSharp.Deserializers;
using System.ComponentModel;
using TencentCloud.Cme.V20191029.Models;
using static Dapper.SqlMapper;

namespace QuDao
{
    /// <summary>
    /// 数据表base_user关联的业务对象，处理该表相关的业务逻辑
    /// </summary>
    public sealed partial class UserBO : SingletonBase<UserBO>
    {
        readonly IFreeSql fsql;
        readonly AppSettings appSettings;
        public UserBO() : base()
        {
            fsql = ServiceLocator.Instance.GetRequiredService<IFreeSql>();
            appSettings = AppSettings.Load();
        }

        /// <summary>
        /// 获取用户
        /// </summary>
        /// <param name="id">主键ID的值</param>
        /// <returns></returns>
        public T GetItem<T>(long id)
        {
            if (id <= 0) return default;
            var item = fsql.Select<User>().Where(a => a.UserID == id).ToOne<T>();
            return item;
        }

        public User GetItem(long id)
        {
            return GetItem<User>(id);
        }

        public UserDetailsDto GetDetails(long id)
        {
            var dto = GetItem<UserDetailsDto>(id);
            dto.TenantInfo = TenantBO.Instance.GetItem<TenantDto>(dto.TID);
            return dto; 
        }

        /// <summary>
        /// 获取推荐的会员用户列表（团队成员列表）
        /// </summary>
        /// <param name="tid">租户ID</param>
        /// <param name="recoID">推荐人ID</param>
        /// <returns></returns>
        public List<UserDto> GetRecoList(int tid, long recoID, string userType = "")
        {
            var list = fsql.Select<User>()
                .Where(a => a.TID == tid && a.RecoID == recoID && a.Status > 0)
                .WhereIf(!userType.IsNullOrEmpty(), a => a.UserType == userType)
                .ToList<UserDto>();
            return list;
        }

        /// <summary>
        /// 获取租户最近加入会员用户数据列表
        /// </summary>
        /// <param name="pageIndex">页索引，从1开始</param>
        /// <param name="pageSize">每页记录数，默认10</param>
        /// <returns></returns>
        public List<UserDto> GetListByTID(int tid, int pageIndex = 1, int pageSize = 10)
        {
            var list = fsql.Select<User>()
                .Where(a => a.TID == tid && a.Status > 0)
                .OrderByDescending(a => a.UserID)
                .Page(pageIndex, pageSize)
                .ToList<UserDto>();
            return list;
        }

        /// <summary>
        /// 获取代理用户列表
        /// </summary>
        /// <param name="tid">租户ID</param>
        /// <param name="pageIndex">页索引，从1开始</param>
        /// <param name="pageSize">每页记录数，默认10</param>
        /// <returns></returns>
        public List<UserDto> GetAgentList(int tid, int pageIndex = 1, int pageSize = 200)
        {
            var list = fsql.Select<User>()
                .Where(a => a.TID == tid && a.Status > 0 && a.IsAgent == true)
                .OrderBy(a => a.UserID)
                .Page(pageIndex, pageSize)
                .ToList<UserDto>();
            return list;
        }

        public List<UserCashDto> GetCashList(int tid, int pageIndex = 1, int pageSize = 200)
        {
            var list = fsql.Select<User>()
                .Where(a => a.TID == tid && a.Status > 0 && a.TotalBonus > decimal.Zero)
                .OrderBy(a => a.UserID)
                .Page(pageIndex, pageSize)
                .ToList<UserCashDto>();
            return list;
        }

        /// <summary>
        /// 获取具有管理角色的用户列表
        /// </summary>
        /// <param name="tid">租户ID</param>
        /// <param name="role">管理角色：9-管理员，1-财务，2-仓库</param>
        /// <returns></returns>
        public List<UserDto> GetListByRole(int tid, int role)
        {
            var roles = new int[] { 9, role };
            var select = fsql.Select<User>().Where(a => a.TID == tid && a.Status > 0);
            if (role == 9)
            {
                select.Where(a => a.Role == role);
            }
            else
            {
                select.Where(a => a.Role == 9 || a.Role == role);
            }
            var list = select.ToList<UserDto>();
            return list;
        }

        /// <summary>
        /// 获取会员数量（按类型ABC及总数）
        /// </summary>
        /// <param name="tid"></param>
        /// <returns></returns>
        public Tuple<int,int,int,int> GetCountByType(int tid)
        {
            var list = fsql.Select<User>()
                .Where(a => a.TID == tid)
                .GroupBy(a => a.UserType)
                .ToList(a => new {a.Key, Num = a.Count()});

            int a = 0, b = 0, c = 0, total = 0;
            foreach (var item in list)
            {
                total += item.Num;
                switch (item.Key)
                {
                    case "A":
                        a = item.Num;
                        break;
                    case "B":
                        b = item.Num;
                        break;
                    case "C":
                        c = item.Num;
                        break;
                    default:
                        break;
                }
            }

            return new Tuple<int, int, int, int>(a, b, c, total);
        }

        /// <summary>
        /// 获取所有用户总余额（待提现总金额）
        /// </summary>
        /// <param name="tid"></param>
        /// <returns></returns>
        public decimal GetTotalBalance(int tid)
        {
            var total = fsql.Select<User>()
                .Where(a => a.TID == tid)
                .Sum(a => a.Balance);

            return total;
        }

        /// <summary>
        /// 新增用户
        /// </summary>
        public int Add(UserAddDto dto)
        {
            var tenant = TenantBO.Instance.GetItem(dto.TID) ?? throw new AppException("无效的租户");
            if (tenant.CommissionFlag == 2 && dto.IsAgent && dto.AgentAreaID == 0) throw new AppException("请选择代理城市");
            if (!dto.Mobile.IsMobileNumber()) throw new AppException($"无效的手机号：{dto.Mobile}");

            var item = GetItemByMobile<User>(dto.TID, dto.Mobile);
            if (item != null) throw new AppException($"手机号已被注册：{dto.Mobile}");

            var entity = dto.CopyTo<User>();
            var reco = GetItem(dto.RecoID);
            if(reco != null)
            {
                entity.RecoID = reco.UserID;
                entity.RecoName = reco.TrueName;

                var reco2 = GetItem(reco.RecoID);
                if(reco2 != null)
                {
                    entity.RecoID2 = reco2.UserID;
                    entity.RecoName2 = reco2.TrueName;
                }
            }

            var county = AreaBO.Instance.GetItem(dto.CountyID);
            if (county != null)
            {
                if (county.AreaLevel != 3) throw new AppException("请选择所属区县，而不是选择省/市级");
                entity.CountyID = county.AreaID;
                entity.CountyName = county.AreaName;

                var city = AreaBO.Instance.GetCity(county.ParentID);
                entity.CityID = city.AreaID;
                entity.CityName = city.AreaName;

                var province = AreaBO.Instance.GetProvince(city.ParentID);
                entity.ProvinceID = province.AreaID;
                entity.ProvinceName = province.AreaName;
            }

            entity.UserID = GetNewID();
            entity.Status = 1;
            entity.RegTime = DateTime.Now;
            entity.ModifyTime = DateTime.Now;
            return fsql.Insert(entity).ExecuteAffrows();
        }

        /// <summary>
        /// 修改用户
        /// </summary>
        public int Modify(UserModifyDto dto)
        {
            var item = GetItem<User>(dto.UserID) ?? throw new AppException($"用户不存在：{dto.UserID}");
            var tenant = TenantBO.Instance.GetItem(item.TID);
            if (tenant.CommissionFlag == 2 && dto.IsAgent && dto.AgentAreaID == 0) throw new AppException("请选择代理城市");

            var update = fsql.Update<User>(dto.UserID).Set(a => a.ModifyTime, DateTime.Now);

            if (dto.UserType != item.UserType) update.Set(a => a.UserType, dto.UserType);
            if (dto.Role != item.Role) update.Set(a => a.Role, dto.Role);
            if (dto.TrueName != item.TrueName) update.Set(a => a.TrueName, dto.TrueName);
            if (dto.CustomerName != item.CustomerName) update.Set(a => a.CustomerName, dto.CustomerName);
            if (dto.Sex != item.Sex) update.Set(a => a.Sex, dto.Sex);
            if (dto.IsAgent != item.IsAgent) update.Set(a => a.IsAgent, dto.IsAgent);
            if (dto.AgentAreaID != item.AgentAreaID) update.Set(a => a.AgentAreaID, dto.AgentAreaID);
            if (dto.ReceiverPhone != item.ReceiverPhone) update.Set(a => a.ReceiverPhone, dto.ReceiverPhone);
            if (dto.ReceiverName != item.ReceiverName) update.Set(a => a.ReceiverName, dto.ReceiverName);
            if (dto.ReceiveAddress != item.ReceiveAddress) update.Set(a => a.ReceiveAddress, dto.ReceiveAddress);
            if (dto.ClearingBank != item.ClearingBank) update.Set(a => a.ClearingBank, dto.ClearingBank);
            if (dto.BankAccountName != item.BankAccountName) update.Set(a => a.BankAccountName, dto.BankAccountName);
            if (dto.BankCardNo != item.BankCardNo) update.Set(a => a.BankCardNo, dto.BankCardNo);
            if (dto.Memo != item.Memo) update.Set(a => a.Memo, dto.Memo);

            if (dto.Mobile != item.Mobile) 
            {
                var user = GetItemByMobile<User>(item.TID, dto.Mobile);
                if (user != null) throw new AppException($"手机号已被注册：{dto.Mobile}");

                update.Set(a => a.Mobile, dto.Mobile);
            }

            if (dto.RecoID != item.RecoID)
            {
                var reco = GetItem(dto.RecoID);
                if (reco != null)
                {
                    update.Set(a => a.RecoID, reco.UserID).Set(a => a.RecoName, reco.TrueName);

                    var reco2 = GetItem(reco.RecoID);
                    if (reco2 != null) update.Set(a => a.RecoID2, reco2.UserID).Set(a => a.RecoName2, reco2.TrueName);
                }
                else
                {
                    update.Set(a => a.RecoID, 0).Set(a => a.RecoName, string.Empty).Set(a => a.RecoID2, 0).Set(a => a.RecoName2, string.Empty);
                }
            }
            if (dto.CountyID != item.CountyID)
            {
                var county = AreaBO.Instance.GetArea(dto.CountyID);
                if (county.AreaLevel != 3) throw new AppException("请选择所属区县，而不是选择省/市级");
                if (county != null)
                {
                    var city = AreaBO.Instance.GetCity(county.ParentID);
                    var province = AreaBO.Instance.GetProvince(city.ParentID);
                    update.Set(a => a.CountyID, dto.CountyID).Set(a => a.CountyName, county.AreaName)
                        .Set(a => a.CityID, city.AreaID).Set(a => a.CityName, city.AreaName)
                        .Set(a => a.ProvinceID, province.AreaID).Set(a => a.ProvinceName, province.AreaName);
                }
            }


            return update.ExecuteAffrows();
        }


        /// <summary>
        /// 后台查询数据（分页）
        /// </summary>
        /// <param name="req">分页查询请求对象</param>
        public PagingResult<T> Query<T>(PagingRequest req, int tid)
        {
            var select = fsql.Select<User>().Where(a => a.Status >= 0).WhereIf(tid > 0, a => a.TID == tid);
            foreach (var item in req.FilterItems)
            {
                switch (item.Field)
                {
                    case "UserID":
                        select.Where(a => a.UserID == long.Parse(item.Value));
                        break;
                    case "TrueName":
                        select.Where(a => a.TrueName.StartsWith(item.Value));
                        break;
                    case "Mobile":
                        select.Where(a => a.Mobile.StartsWith(item.Value));
                        break;
                    default:
                        break;
                }
            }

            if (!string.IsNullOrEmpty(req.SortExp))
            {
                select.OrderBy(req.SortExp);//自定义排序
            }
            else
            {
                select.OrderByDescending(a => a.UserID);//默认排序
            }

            var sql = select.ToSql();

            var list = select.Count(out var total).Page(req.PageIndex, req.PageSize).ToList<T>();
            return new PagingResult<T>
            {
                PageIndex = req.PageIndex,
                PageSize = req.PageSize,
                Data = list,
                Total = (int)total
            };
        }

        public PagingResult<User> Query(PagingRequest req, int tid)
        {
            return Query<User>(req, tid);
        }

        public PagingResult<UserCashDto> QueryByCash(PagingRequest req, int tid)
        {
            var select = fsql.Select<User>().Where(a => a.TID == tid && a.Status >= 0 && a.TotalBonus > decimal.Zero);
            foreach (var item in req.FilterItems)
            {
                switch (item.Field)
                {
                    case "UserID":
                        select.Where(a => a.UserID == long.Parse(item.Value));
                        break;
                    case "TrueName":
                        select.Where(a => a.TrueName.StartsWith(item.Value));
                        break;
                    case "Mobile":
                        select.Where(a => a.Mobile.StartsWith(item.Value));
                        break;
                    default:
                        break;
                }
            }

            if (!string.IsNullOrEmpty(req.SortExp))
            {
                select.OrderBy(req.SortExp);//自定义排序
            }
            else
            {
                select.OrderByDescending(a => a.UserID);//默认排序
            }

            var sql = select.ToSql();

            var list = select.Count(out var total).Page(req.PageIndex, req.PageSize).ToList<UserCashDto>();
            return new PagingResult<UserCashDto>
            {
                PageIndex = req.PageIndex,
                PageSize = req.PageSize,
                Data = list,
                Total = (int)total
            };
        }

        /// <summary>
        /// 获取代理用户信息
        /// </summary>
        /// <param name="user">当前进货下单的会员用户</param>
        /// <param name="tenant"></param>
        /// <returns></returns>
        public UserInfoDto GetAgent(User user, Tenant tenant)
        {
            if(tenant.CommissionFlag == 1)
            {
                var reco = user.CopyTo<UserInfoDto>();
                var list = fsql.Select<User>().Where(a => a.TID == tenant.TenantID).ToList<UserInfoDto>();
                while (true)
                {
                    if(reco.IsAgent)return reco;
                    if (reco.RecoID == 0) return null;

                    reco = list.Find(p => p.UserID == reco.RecoID);
                }
            }
            else if(tenant.CommissionFlag == 2)
            {
                if (user.CityID == 0) return null;

                var agent = fsql.Select<User>().Where(a => a.IsAgent == true && a.AgentAreaID == user.CityID).ToOne<User>();
                if(agent != null)
                {
                    return agent.CopyTo<UserInfoDto>();
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 根据手机号获取用户信息
        /// </summary>
        public T GetItemByMobile<T>(int tid, string mobile)
        {
            return fsql.Select<User>().Where(a => a.TID == tid && a.Mobile == mobile).ToOne<T>();
        }

        /// <summary>
        /// 判断手机号是否存在（是否绑定）
        /// </summary>
        public bool IsExistMobile(int tid, string mobile)
        {
            return fsql.Select<User>().Any(a => a.TID == tid && a.Mobile == mobile);
        }

        public static long GetNewID()
        {
            var id = IDGenerator.Instance.GetIDByH1024() * 10 + RandomUtil.GetInteger(0, 9);
            return id;
        }

        /// <summary>
        /// 用户登录（支持多应用）
        /// </summary>
        /// <param name="tid">租户ID</param>
        /// <param name="mobile">手机号</param>
        /// <param name="verifyCode"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="AppException"></exception>
        public User Login(int tid, string mobile, string verifyCode = "")
        {
            if (!mobile.IsMobileNumber()) throw new AppException("手机号码无效");

            ///todo:判断验证码是否正确

            var user = GetItemByMobile<User>(tid, mobile) ?? throw new AppException($"账号不存在：{mobile}");
            if (user.Status != 1) throw new AppException("无效的账号");

            var update = fsql.Update<User>(user.UserID)
                    //.Set(a => a.Nickname, dto.Nickname)
                    //.Set(a => a.Sex, dto.Gender == 0 ? "" : (dto.Gender == 1 ? "男" : "女"))
                    //.Set(a => a.AvatarUrl, dto.AvatarUrl)
                    .Set(a => a.LoginCount + 1)
                    .Set(a => a.LastLoginTime, DateTime.Now)
                    .Set(a => a.ModifyTime, DateTime.Now);

            update.ExecuteAffrows();

            return user;
        }

        /// <summary>
        /// 更新指定字段值（支持字段：TrueName-姓名，Mobile-手机号，AvatarUrl-头像，ReceiverName-收货人姓名，ReceiverPhone-收货人电话，ReceiveAddress-收货人地址）
        /// </summary>
        /// <param name="id">用户ID</param>
        /// <param name="fieldName">字段名</param>
        /// <param name="fieldValue">字段值</param>
        /// <returns></returns>
        public int Update(long id, string fieldName, string fieldValue)
        {
            var n = 0;
            switch (fieldName)
            {
                case "Mobile":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.Mobile, fieldValue).ExecuteAffrows();
                    break;
                case "TrueName":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.TrueName, fieldValue).ExecuteAffrows();
                    break;
                case "AvatarUrl":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.AvatarUrl, fieldValue).ExecuteAffrows();
                    break;
                case "ReceiverName":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.ReceiverName, fieldValue).ExecuteAffrows();
                    break;
                case "ReceiverPhone":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.ReceiverPhone, fieldValue).ExecuteAffrows();
                    break;
                case "ReceiveAddress":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.ReceiveAddress, fieldValue).ExecuteAffrows();
                    break;
                case "AccessToken":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.AccessToken, fieldValue).ExecuteAffrows();
                    break;
                case "ClearingBank":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.ClearingBank, fieldValue).ExecuteAffrows();
                    break;
                case "BankAccountName":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.BankAccountName, fieldValue).ExecuteAffrows();
                    break;
                case "BankCardNo":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id).Set(a => a.BankCardNo, fieldValue).ExecuteAffrows();
                    break;
                case "Status":
                    if (fieldValue.IsNullOrEmpty()) break;
                    n = fsql.Update<User>(id)
                        .Set(a => a.Status, int.Parse(fieldValue))
                        .SetIf(fieldValue == "-1", a => a.Mobile + $"-{DateTime.Now:yyMMdd}") //防止后面该手机号不能再次注册
                        .ExecuteAffrows();
                    break;
                default:
                    break;
            }

            return n;
        }

        /// <summary>
        /// 更新结算银行账户信息
        /// </summary>
        /// <param name="id">会员用户ID</param>
        /// <param name="bankName">结算银行名称</param>
        /// <param name="accountName">开户名称（姓名或单位）</param>
        /// <param name="bankCardNo">开户银行卡号</param>
        /// <returns></returns>
        public int UpdateBankInfo(long id, string bankName, string accountName, string bankCardNo)
        {
            return fsql.Update<User>(id)
                .Set(a => a.ClearingBank, bankName)
                .Set(a => a.BankAccountName, accountName)
                .Set(a => a.BankCardNo, bankCardNo)
                .ExecuteAffrows();
        }

        /// <summary>
        /// 更新用户所属区域信息（省市区/县）
        /// </summary>
        public void UpdateArea(long id, int provinceID, string provinceName, int cityID, string cityName, int countyID, string countyName)
        {
            fsql.Update<User>(id)
                .Set(a => a.ProvinceID, provinceID)
                .Set(a => a.ProvinceName, provinceName)
                .Set(a => a.CityID, cityID)
                .Set(a => a.CityName, cityName)
                .Set(a => a.CountyID, countyID)
                .Set(a => a.CountyName, countyName)
                .ExecuteAffrows();
        }


    }
}
